package com.whmall.service.crm.algorithm;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.whmall.component.entity.dto.CompanyKeyFieldDTO;
import com.whmall.component.entity.dto.QueryManagerReqDTO;
import com.whmall.component.entity.dto.QueryManagerRspDTO;
import com.whmall.component.entity.dto.StatisticCustomerNumber4TraderDTO;
import com.whmall.component.enumeration.*;
import com.whmall.component.im.constant.NoticeCategoryConstant;
import com.whmall.component.im.constant.NoticeContentTypeConstant;
import com.whmall.component.im.constant.NoticeOperationTypeConstant;
import com.whmall.component.im.entity.EmployeeNoticeDTO;
import com.whmall.component.util.DateUtils;
import com.whmall.service.common.api.CalendarApi;
import com.whmall.service.common.api.MessageApi;
import com.whmall.service.crm.bo.CustomerPoolGradeNumberLimit;
import com.whmall.service.crm.constant.CompanyConstant;
import com.whmall.service.crm.entity.dto.customerpool.CustomerPoolScoreTimeConfigRspDTO;
import com.whmall.service.crm.enumeration.CustomerFollowStatusEnum;
import com.whmall.service.crm.enumeration.CustomerPoolOperationTypeEnum;
import com.whmall.service.crm.module.company.dao.CompanyLogMapper;
import com.whmall.service.crm.module.company.dao.CompanyMapper;
import com.whmall.service.crm.module.company.entity.CompanyDO;
import com.whmall.service.crm.module.company.entity.CompanyLogDO;
import com.whmall.service.crm.module.company.entity.qo.CompanyQO;
import com.whmall.service.crm.module.company.service.CompanyAlgorithm;
import com.whmall.service.crm.module.company.service.CompanyIndustryTypeService;
import com.whmall.service.crm.module.customerpool.dao.CustomerPoolFollowMapper;
import com.whmall.service.crm.module.customerpool.dao.CustomerPoolLogMapper;
import com.whmall.service.crm.module.customerpool.entity.CustomerPoolFollowDO;
import com.whmall.service.crm.module.customerpool.entity.CustomerPoolLogDO;
import com.whmall.service.crm.module.customerpool.entity.qo.CustomerPoolFollowQO;
import com.whmall.service.system.api.EmployeeApi;
import com.whmall.service.system.entity.dto.employee.EmployeeDigestRspDTO;
import com.whmall.service.system.entity.dto.employee.EmployeeQueryReqDTO;
import com.whmall.service.system.entity.dto.employee.EmployeeRspDTO;
import com.whmall.service.system.entity.dto.employee.EmployeeWithPowerRspDTO;
import com.whmall.service.system.module.employee.enumeration.EmployeeStatusEnum;
import com.whmall.service.system.module.employee.enumeration.PositionEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 客户池相关算法
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CustomerPoolAlgorithm {

    private final CompanyMapper companyMapper;
    private final CompanyIndustryTypeService companyIndustryTypeService;
    private final CustomerPoolFollowMapper customerPoolFollowMapper;
    private final CustomerPoolLogMapper customerPoolLogMapper;
    private final StringRedisTemplate stringRedisTemplate;
    private final CompanyLogMapper companyLogMapper;
    private final CompanyAlgorithm companyAlgorithm;
    @DubboReference
    private MessageApi messageApi;
    @DubboReference
    private CalendarApi calendarApi;
    @DubboReference
    private EmployeeApi employeeApi;

    public static final Map<String, Integer> MAPPER = new HashMap<String, Integer>() {{
        put("S", 0);
        put("A", 0);
        put("B", 0);
        put("C", 0);
        put("D", 0);
        put("E", 0);
    }};


    /**
     * 判断客户的未下单天数能否进入客户池
     */
    public Map<Long, Integer> canEnterCustomerPool(List<Long> companyIds) {
        if (CollectionUtils.isEmpty(companyIds)) {
            return Collections.emptyMap();
        }
        return companyMapper.getByIds(companyIds)
                .stream().collect(Collectors.toMap(
                        CompanyDO::getId,
                        e -> DateUtils.daysDifference(
                                DateUtils.getDayBegin(e.getLastOrderTime()),
                                DateUtils.getDayBegin(new Date())) > CompanyConstant.CUSTOMER_POOL_NO_ORDER_DAYS
                                ? 1 : 0));
    }

    /**
     * 获取评分时间配置
     */
    public CustomerPoolScoreTimeConfigRspDTO getScoreTimeConfig(Date now) {
        Calendar c = Calendar.getInstance();
        try {
            c.setTime(new SimpleDateFormat("yyyy-MM-dd").parse("2020-12-31"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        now = now != null ? now : new Date();
        List<Date> quarterLastDays = new ArrayList<>();
        while (c.getTime().before(now)) {
            if (c.get(Calendar.MONTH) % 3 == 2) {
                quarterLastDays.add(c.getTime());
                c.add(Calendar.MONTH, 3);
            }
        }
        // 全部季度
        List<String> scoreQuarters = quarterLastDays.stream().map(e -> {
            c.setTime(e);
            return new SimpleDateFormat("yyyy").format(e) + "年Q" + (c.get(Calendar.MONTH) / 3 + 1);
        }).sorted(Comparator.reverseOrder()).collect(Collectors.toList());
        // 最近季度
        Date endTime = c.getTime();
        c.add(Calendar.MONTH, -11);
        Date startTime = DateUtils.getMonthBegin(c.getTime());
        return CustomerPoolScoreTimeConfigRspDTO.builder().scoreQuarters(scoreQuarters)
                .scoreQuarter(scoreQuarters.get(0)).startTime(startTime).endTime(endTime).build();
    }

    /**
     * 自动释放客户-数量超限的
     */
    @Transactional(rollbackFor = Exception.class)
    public void autoReleaseCustomer(Map<Long, QueryManagerRspDTO> traders, Map<Long, Date> employeeNotices) {
        Map<Long, StatisticCustomerNumber4TraderDTO> traderStatistics = statisticTraderCustomerNumber(traders);
        Map<Long, List<CustomerPoolFollowDO>> followMap = customerPoolFollowMapper.selectBy(
                        CustomerPoolFollowQO.builder()
                                .followStatus(CustomerFollowStatusEnum.TO_BE_FOLLOWED.getValue())
                                .orderBy("id desc")
                                .build())
                .stream().collect(Collectors.groupingBy(CustomerPoolFollowDO::getTraderId, Collectors.toList()));
        Set<Long> companyIdsReleased = new HashSet<>();
        for (StatisticCustomerNumber4TraderDTO trader : traderStatistics.values()) {
            Map<String, Integer> needRelease = trader.isNeedRelease(null);
            if (needRelease.isEmpty()) {
                continue;
            }
            /*--------------------------发送消息begin-------------------------*/
            if (!employeeNotices.containsKey(trader.getTraderId())) {
                StringBuilder content = new StringBuilder("名下客户超上限数，");
                needRelease.forEach((key, value) -> {
                    if (key.equals("ALL")) {
                        key = "总客户数";
                    } else {
                        key = key + "类客户数";
                    }
                    content.append(StrUtil.format("{}超出客户数为{}个，", key, value));
                });
                content.append("请及时释放客户。");
                // 给撮合发消息
                messageApi.sendMsg(EmployeeNoticeDTO.builder()
                        .noticeCategory(NoticeCategoryConstant.REMIND)
                        .noticeType(EmployeeNoticeTypeEnum.RELEASE_CUSTOMER_4_TRADER.getValue())
                        .noticeContentType(NoticeContentTypeConstant.TEXT)
                        .noticeOperationType(NoticeOperationTypeConstant.REDIRECT_TO_LIST)
                        .noticeOperationValue("{}")
                        .content(content.toString())
                        .relateNo(trader.getTraderId().toString())
                        .employeeIds(Collections.singletonList(trader.getTraderId()))
                        .createId(0L)
                        .createType(OperatorTypeEnum.SYSTEM.getValue())
                        .build());
                continue;
            }
            int traderNoticeDays = calendarApi.daysDifferenceInWorktime(
                    employeeNotices.get(trader.getTraderId()), new Date());
            // 等待撮合处理
            if (traderNoticeDays < 2) {
                continue;
            } else if (traderNoticeDays == 2) {
                // 第3天给主管发消息
                if (!CollectionUtils.isEmpty(traders.get(trader.getTraderId()).getManagers())) {
                    // DEBUG: 这里可能无法获得EmployeeRspDTO对象，需测试
                    EmployeeRspDTO employee = employeeApi.getById(trader.getTraderId());
                    StringBuilder content = new StringBuilder("撮合");
                    content.append(Optional.ofNullable(employee).map(EmployeeRspDTO::getName).orElse(""))
                            .append("名下客户超上限数，");
                    needRelease.forEach((key, value) -> {
                        if (key.equals("ALL")) {
                            key = "总客户数";
                        } else {
                            key = key + "类客户数";
                        }
                        content.append(StrUtil.format("{}超出客户数为{}个，", key, value));
                    });
                    content.append("请及时释放客户。");
                    messageApi.sendMsg(EmployeeNoticeDTO.builder()
                            .noticeCategory(NoticeCategoryConstant.REMIND)
                            .noticeType(EmployeeNoticeTypeEnum.RELEASE_CUSTOMER_4_MANAGER.getValue())
                            .noticeContentType(NoticeContentTypeConstant.TEXT)
                            .noticeOperationType(NoticeOperationTypeConstant.REDIRECT_TO_LIST)
                            .noticeOperationValue("{}")
                            .content(content.toString())
                            .employeeIds(Collections.singletonList(traders.get(trader.getTraderId()).getManagers().get(0).getId()))
                            .createId(0L)
                            .createType(OperatorTypeEnum.SYSTEM.getValue())
                            .build());
                }
                continue;
            } else if (traderNoticeDays < 5) {
                // 等待撮合主管处理
                continue;
            }
            /*----------------------发送消息end-------------------------*/
            if (trader.isNeedRelease(null).isEmpty()) {
                // 没有超上限的，不进行释放
                return;
            }


            // 此处应该从E类客户开发栏开始释放
            List<CompanyKeyFieldDTO> ownCompanys = trader.getOwnCompanys()
                    .stream().sorted((a, b) -> MAPPER.getOrDefault(b.getCustomerGrade(), 6) - MAPPER.getOrDefault(a.getCustomerGrade(), 6))
                    .collect(Collectors.toList());
            // 自己名下的全部公司，公司ID为key
            Map<Long, CustomerPoolFollowDO> ownFollows = followMap.getOrDefault(trader.getTraderId(), Collections.emptyList())
                    .stream().collect(Collectors.toMap(CustomerPoolFollowDO::getCompanyId, e -> e, (e1, e2) -> e1));
            // 1、单项超限，释放开发客户栏
            // 2、单项超限，释放非开发客户栏
            // 3、总数超限，释放开发客户栏（优先释放低级别，sql体现）
            // 4、总数超限，释放非开发客户栏（优先释放低级别，sql体现）

            int i = 0;
            while (i++ < 4) {
                for (CompanyKeyFieldDTO ownCompany : ownCompanys) {
                    if (companyIdsReleased.contains(ownCompany.getId())) {
                        continue;
                    }
                    if ((i == 1 || i == 3) && !ownFollows.containsKey(ownCompany.getId())) {
                        continue;
                    }
                    if ((i == 1 || i == 2) ? trader.isNeedRelease(ownCompany.getCustomerGrade()).containsKey(ownCompany.getCustomerGrade()) :
                            !trader.isNeedRelease(null).isEmpty()) {
                        dbOperation(ownCompany.getId(), ownFollows.get(ownCompany.getId()));
                        trader.releaseOne(ownCompany.getCustomerGrade());
                        companyIdsReleased.add(ownCompany.getId());
                    }
                }
                if (trader.isNeedRelease(null).isEmpty()) {
                    // 当全部都满足不超上限，直接停止遍历
                    return;
                }
            }
        }
    }

    /**
     * 自动释放客户-数据库操作
     */
    private void dbOperation(Long companyId, CustomerPoolFollowDO follow) {
        if (follow != null) {
            customerPoolFollowMapper.updateByIdSelective(
                    CustomerPoolFollowDO.builder().id(follow.getId())
                            .followStatus(CustomerFollowStatusEnum.RELEASED.getValue())
                            .updateTime(new Date()).build()
            );
            customerPoolLogMapper.insertSelective(
                    CustomerPoolLogDO.builder()
                            .companyId(follow.getCompanyId())
                            .operationType(CustomerPoolOperationTypeEnum.RELEASE_CUSTOMER.getValue())
                            .remark("撮合客户数量超限，系统自动释放")
                            .handleType(OperatorTypeEnum.SYSTEM.getValue())
                            .handleId(0L)
                            .handleTime(new Date()).build()
            );
        }


        CompanyDO companyDO = companyMapper.selectById(companyId);


        List<QueryManagerRspDTO> queryManagerRspDTOS = employeeApi.queryManagers(QueryManagerReqDTO.builder().build());
        List<Long> employees = queryManagerRspDTOS.stream().filter(e ->
                e.getPower().equals(PowerEnum.TRADER.getValue())
                        && e.getEmployeeStatus() == EmployeeStatusEnum.IN_SERVICE.getValue()
                        && e.getIsCaocno()
        ).map(QueryManagerRspDTO::getId).collect(Collectors.toList());
        Long traderId = companyAlgorithm.getOneManager(PositionEnum.TRADER.getValue(), false, null, employees);
        EmployeeRspDTO employeeRspDTO = employeeApi.getById(traderId);
        Assert.notNull(employeeRspDTO, "员工" + traderId + "不存在");

        companyMapper.updateByIds(
                CompanyQO.builder().ids(Collections.singletonList(companyId))
                        .traderId(employeeRspDTO.getId())
                        .traderDepartmentPath(employeeRspDTO.getDepartmentPath())
                        .updateTime(new Date()).build()
        );


        // 公司日志
        CompanyLogDO logDO = CompanyLogDO.builder()
                .companyId(companyDO.getId())
                .createType(OperatorTypeEnum.SYSTEM.getValue())
                .createId(0L)
                .operationType(CompanyLogOperationTypeEnum.CUSTOMER_POOL_OPERATION.getValue())
                .operationDesc1("释放公司，原因“撮合客户数量超限，系统自动释放”")
                .ip(null).createTime(new Date()).build();
        companyLogMapper.insertSelective(logDO);
        sendMessage(companyDO, messageApi);

    }

    public static void sendMessage(CompanyDO companyDO, MessageApi messageApi) {
        messageApi.sendMsg(EmployeeNoticeDTO.builder()
                .noticeCategory(NoticeCategoryConstant.REMIND)
                .noticeType(EmployeeNoticeTypeEnum.RELEASE_CUSTOMER.getValue())
                .noticeContentType(NoticeContentTypeConstant.TEXT)
                .noticeOperationType(NoticeOperationTypeConstant.REDIRECT_TO_DETAIL)
                .noticeOperationValue(String.format("{\"key\":\"%s\"}", companyDO.getId()))
                .content(companyDO.getName() + "已被释放")
                .relateNo(companyDO.getId().toString())
                .employeeIds(Collections.singletonList(companyDO.getTraderId()))
                .createId(0L)
                .createType(OperatorTypeEnum.SYSTEM.getValue())
                .build());
    }

    /**
     * 统计撮合的客户数量
     */
    public Map<Long, StatisticCustomerNumber4TraderDTO> statisticTraderCustomerNumber(Map<Long, QueryManagerRspDTO> traders) {
        Assert.isTrue(!CollectionUtils.isEmpty(traders), "参数错误");
        List<CustomerPoolGradeNumberLimit> gradeNumberLimits = getGradeNumberLimit();
        List<CompanyDO> companies = companyMapper.statisticTraderCustomerNumber(
                CompanyQO.builder().traderIds(new ArrayList<>(traders.keySet())).build());

        // 公司业务性质

        List<CompanyDO> companies_2 = companies.stream()
                .filter(e -> {
                    // 锁定的不要
                    return !Objects.equals(e.getIsLockPersonalPoolCustomer(), 1);
                })
                .collect(Collectors.toList());
        return companies_2.stream()
                .collect(Collectors.groupingBy(CompanyDO::getTraderId, Collectors.mapping(e -> {
                    CompanyKeyFieldDTO item = new CompanyKeyFieldDTO();
                    BeanUtils.copyProperties(e, item);
                    return item;
                }, Collectors.toList())))
                .entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> {
                    QueryManagerRspDTO employee = traders.get(entry.getKey());
                    StatisticCustomerNumber4TraderDTO dto = new StatisticCustomerNumber4TraderDTO();
                    dto.setTraderId(entry.getKey());
                    dto.setOwnCompanys(entry.getValue());
                    dto.setTraderGrade(employee.getTraderGrade());
                    for (CompanyKeyFieldDTO item : dto.getOwnCompanys()) {
                        dto.setAGradeNumber(dto.getAGradeNumber() +
                                (item.getCustomerGrade().equals(CustomerGradeEnum.A.getValue()) ? 1 : 0));
                        dto.setBGradeNumber(dto.getBGradeNumber() +
                                (item.getCustomerGrade().equals(CustomerGradeEnum.B.getValue()) ? 1 : 0));
                        dto.setCGradeNumber(dto.getCGradeNumber() +
                                (item.getCustomerGrade().equals(CustomerGradeEnum.C.getValue()) ? 1 : 0));
                        dto.setDGradeNumber(dto.getDGradeNumber() +
                                (item.getCustomerGrade().equals(CustomerGradeEnum.D.getValue()) ? 1 : 0));
                        dto.setEGradeNumber(dto.getEGradeNumber() +
                                (item.getCustomerGrade().equals(CustomerGradeEnum.E.getValue()) ? 1 : 0));
                    }
                    // 限制数量
                    for (CustomerPoolGradeNumberLimit limit : gradeNumberLimits) {
                        if (employee.getTraderGrade() == limit.getTraderGrade().intValue()) {

                            dto.setCGradeNumberLimit(limit.getCustomerGrade().equals(CustomerGradeEnum.C.getValue()) ?
                                    limit.getNumber() : dto.getCGradeNumberLimit());
                            dto.setCGradeNumberLimitInit(limit.getCustomerGrade().equals(CustomerGradeEnum.C.getValue()) ?
                                    limit.getNumber() : dto.getCGradeNumberLimit());

                            dto.setDGradeNumberLimit(limit.getCustomerGrade().equals(CustomerGradeEnum.D.getValue()) ?
                                    limit.getNumber() : dto.getDGradeNumberLimit());
                            dto.setDGradeNumberLimitInit(limit.getCustomerGrade().equals(CustomerGradeEnum.D.getValue()) ?
                                    limit.getNumber() : dto.getDGradeNumberLimit());

                            dto.setEGradeNumberLimit(limit.getCustomerGrade().equals(CustomerGradeEnum.E.getValue()) ?
                                    limit.getNumber() : dto.getEGradeNumberLimit());
                            dto.setEGradeNumberLimitInit(limit.getCustomerGrade().equals(CustomerGradeEnum.E.getValue()) ?
                                    limit.getNumber() : dto.getEGradeNumberLimit());

                            dto.setAllGradeNumberLimit(limit.getCustomerGrade().equals(CustomerGradeEnum.ALL.getValue()) ?
                                    limit.getNumber() : dto.getEGradeNumberLimit());
                        }
                    }
                    dto.setTraderCustomerLimit(employee.getTraderCustomerLimit());
                    return dto;
                }));
    }

    /**
     * 获取客户池等级数量限制
     */
    public List<CustomerPoolGradeNumberLimit> getGradeNumberLimit() {
        Map<Object, Object> map = stringRedisTemplate.opsForHash().entries("$dict$customer_pool_grade_number_limit");
        return map.entrySet().stream().map(e -> {
                    CustomerPoolGradeNumberLimit definition = new CustomerPoolGradeNumberLimit();
                    JsonObject jsonObject = (JsonObject) JsonParser.parseString(e.getValue().toString());
                    definition.setK(e.getKey().toString());
                    definition.setTraderGrade(Integer.valueOf(definition.getK().split("-")[0]));
                    definition.setCustomerGrade(definition.getK().split("-")[1]);
                    definition.setNumber(jsonObject.get("number").getAsInt());
                    definition.setSequence(jsonObject.get("sequence").getAsInt());
                    return definition;
                }).sorted(Comparator.comparing(CustomerPoolGradeNumberLimit::getTraderGrade)
                        .thenComparing(CustomerPoolGradeNumberLimit::getCustomerGrade))
                .collect(Collectors.toList());
    }

}
